﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Tycho.Runtime;
using System.Transactions;
using System.Threading;
using Tycho.Transactions;

namespace TychoTest.Transactions {
    [TestClass]
    public class TestTransactionalFrame {
        TransactionalDynamicFrameObject Frame;

        [TestInitialize]
        public void Initialize () {
            Frame = new TransactionalDynamicFrameObject (RuntimeModule.Null);
            Frame.Transactionalise ();
        }

        [TestMethod]
        public void TestRollBack () {
            using (TransactionScope tx = new TransactionScope ()) {
                Frame ["something"] = "idiot";
            }

            using (TransactionScope tx = new TransactionScope ()) {
                Assert.IsFalse (Frame.HasVariable ("something"));
            }
        }

        [TestMethod]
        public void TestNonTransactionalRollBack () {
            Frame = new TransactionalDynamicFrameObject (RuntimeModule.Null);

            using (TransactionScope tx = new TransactionScope ()) {
                Frame ["something"] = "idiot";
            }

            using (TransactionScope tx = new TransactionScope ()) {
                Assert.IsTrue (Frame.HasVariable ("something"));
            }
        }

        [TestMethod]
        public void TestCommit () {
            using (TransactionScope tx = new TransactionScope ()) {
                Frame ["something"] = "idiot";
                tx.Complete ();
            }

            using (TransactionScope tx = new TransactionScope ()) {
                Assert.AreEqual ("idiot", Frame ["something"]);
            }
        }

        [TestMethod]
        public void TestConflictNewVariable () {
            Thread alsoAddVariable = new Thread (() => {
                using (TransactionScope tx = new TransactionScope ()) {
                    Frame ["x"] = "one";
                    tx.Complete ();
                }
            });

            try {
                using (TransactionScope tx = new TransactionScope ()) {
                    Frame ["x"] = "two";

                    alsoAddVariable.Start ();
                    alsoAddVariable.Join ();

                    tx.Complete ();
                }

                Assert.Fail ("expected thread to abort");
            } catch (TransactionAbortedException) {
            }
        }

        [TestMethod]
        public void TestNoConflictNewDifferentVariables () {
            Thread alsoAddVariable = new Thread (() => {
                using (TransactionScope tx = new TransactionScope ()) {
                    Frame ["x"] = "one";
                    tx.Complete ();
                }
            });

            using (TransactionScope tx = new TransactionScope ()) {
                Frame ["y"] = "two";

                alsoAddVariable.Start ();
                alsoAddVariable.Join ();

                tx.Complete ();
            }
        }

        [TestMethod]
        public void TestConflictSameVariable () {
            using (TransactionScope tx = new TransactionScope ()) {
                Frame ["x"] = "zero";
                tx.Complete ();
            }

            TestConflictNewVariable ();
        }

        [TestMethod]
        public void TestOneReaderOneWriterNoConflict () {
            using (TransactionScope tx = new TransactionScope ()) {
                Frame ["x"] = "zero";
                tx.Complete ();
            }

            using (TransactionScope tx = new TransactionScope ()) {
                Assert.AreEqual ("zero", Frame ["x"]);
                tx.Complete ();
            }

            Thread alsoAddVariable = new Thread (() => {
                using (TransactionScope tx = new TransactionScope ()) {
                    string x = (string) Frame ["x"];
                    tx.Complete ();
                }
            });

            using (TransactionScope tx = new TransactionScope ()) {
                Frame ["x"] = "two";

                alsoAddVariable.Start ();
                alsoAddVariable.Join ();

                tx.Complete ();
            }

            using (TransactionScope tx = new TransactionScope ()) {
                Assert.AreEqual ("two", Frame ["x"]);
                tx.Complete ();
            }
        }
    }
}
